package com.facebook.swift.codec;

import com.facebook.swift.codec.ThriftCodec;
import com.facebook.swift.codec.ThriftCodecManager;
import com.facebook.swift.codec.internal.ThriftCodecFactory;
import com.facebook.swift.codec.internal.compiler.DynamicClassLoader;
import com.facebook.swift.codec.internal.compiler.ThriftCodecByteCodeGenerator;
import com.facebook.swift.codec.internal.reflection.ReflectionThriftStructCodec;
import com.facebook.swift.codec.internal.reflection.ReflectionThriftUnionCodec;
import com.facebook.swift.codec.metadata.ThriftStructMetadata;

import javax.annotation.concurrent.Immutable;

import static java.lang.String.format;

import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * 提供支持<a href="https://gitee.com/l0km/sql2java">sql2java</a> gu.sql2java.BaseBean 对象编解码器实例
 * 的{@link ThriftCodecFactory}工厂类实现
 * @author guyadong
 */
@Immutable
public class FlexibleThriftCodecFactory implements ThriftCodecFactory
{
	boolean useCompiler = true;
	/** class for gu.sql2java.BaseBean  */
	private Class<?> baseBeanClass = null;
	/**
	 * 构造方法
	 * @param useCompiler 为{@code true}默认使用ThriftCodecByteCodeGenerator生成的编解码器,
	 * 									  否则使用反射实现的编解码器
	 */
	public FlexibleThriftCodecFactory(boolean useCompiler) {
    	this.useCompiler = useCompiler;
		try {
			baseBeanClass = Class.forName("gu.sql2java.BaseBean",false,this.getClass().getClassLoader());
		} catch (ClassNotFoundException e) {
			// BaseBean 不存在时也不会有基于BaseBean的对象
		}
	}
	/**
	 * 默认构造方法,默认使用默认使用ThriftCodecByteCodeGenerator生成的编解码器
	 */
	public FlexibleThriftCodecFactory(){
		this(false);
	}
	@Override
    public ThriftCodec<?> generateThriftTypeCodec(ThriftCodecManager codecManager, ThriftStructMetadata metadata)
    {
        switch (metadata.getMetadataType()) {
            case STRUCT:
            	Type structType = metadata.getStructType();
            	if(structType instanceof Class<?> && null != baseBeanClass && baseBeanClass.isAssignableFrom((Class<?>)structType)){
            		return new BaseBeanReflectionCodec<>(codecManager, metadata);            		
            	}else if(useCompiler){
            			return new ThriftCodecByteCodeGenerator<>(
            	                codecManager,
            	                metadata,
            	                getPriviledgedClassLoader(FlexibleThriftCodecFactory.class.getClassLoader()),
            	                false
            	        ).getThriftCodec();
            		}else {						
            			return new ReflectionThriftStructCodec<>(codecManager, metadata);
					}
            case UNION:
            	if(useCompiler){
            		return new ThriftCodecByteCodeGenerator<>(
            				codecManager,
            				metadata,
            				getPriviledgedClassLoader(FlexibleThriftCodecFactory.class.getClassLoader()),
            				false
            				).getThriftCodec();            			
            	}            	
                return new ReflectionThriftUnionCodec<>(codecManager, metadata);
            default:
                throw new IllegalStateException(format("encountered type %s", metadata.getMetadataType()));
        }
    }
	private static DynamicClassLoader getPriviledgedClassLoader(final ClassLoader parent)
    {
        return AccessController.doPrivileged(new PrivilegedAction<DynamicClassLoader>() {
            public DynamicClassLoader run() {
                return new DynamicClassLoader(parent);
            }
        });
    }
}
